Step 1: L1 part (weighted by ρ):
Step 2: L2 part (weighted by 1 − ρ):
Step 3: Multiply by λ and sum:
Standard linear regression minimizes only the training error and has no incentive to prefer simpler models. The result: complex models with huge coefficients overfit and fail to generalize. In the first half of this unit, we study Regularized Regression — Ridge, Lasso, and Elastic Net — which add a penalty term to the loss function to explicitly trade off performance vs. complexity. In the second half, we introduce Gradient Boosting, one of the most powerful off-the-shelf algorithms for tabular regression and classification.
Consider two candidate models that both fit the training data equally well:
| Model 1 (Simple) | Model 2 (Complex) |
|---|---|
| \( \hat{y} = 2x_3 + 1.4x_7 - 0.5x_9 + 4 \) | \( \hat{y} = 22x_1 - 103x_2 - 14x_3 + 109x_4 - 93x_5 + 203x_6 + 87x_7 - 55x_8 + 378x_9 - 25x_{10} + 8 \) |
We should prefer Model 1 — simpler, sparser, with smaller coefficients. Why are large / many coefficients problematic?
Solution: Add a regularization term to the cost function that penalizes coefficient magnitude. The result: the optimizer is forced to trade off low training error for model simplicity.
Two mathematical ways to penalize coefficient magnitude. Note: only the weights \( \theta_1, \ldots, \theta_p \) are regularized — never the bias \( \theta_0 \).
Used in Lasso Regression. Produces sparse solutions (some coefficients become exactly zero).
Used in Ridge Regression. Shrinks all coefficients towards zero (none hit exactly zero).
Model 1: \( \hat{y} = 2x_3 + 1.4x_7 - 0.5x_9 + 4 \) (weights: 2, 1.4, −0.5; bias 4, not penalized)
Model 2: weights = [22, −103, −14, 109, −93, 203, 87, −55, 378, −25]
Notice how the L2 penalty explodes quadratically for Model 2 — Ridge will really hate Model 2!
The total loss is the sum of (a) regression error (quality of fit) and (b) a regularization term (penalty for complexity):
The hyperparameter \( \lambda \) (lambda) controls the balance between performance and simplicity.
| Aspect | Ridge Regression (L2) | Lasso Regression (L1) |
|---|---|---|
| Full Cost Function | \( J = \frac{1}{2m}\sum(\hat{y}_i-y_i)^2 + \lambda \sum_{j=1}^{p-1} \theta_j^2 \) | \( J = \frac{1}{2m}\sum(\hat{y}_i-y_i)^2 + \lambda \sum_{j=1}^{p-1} |\theta_j| \) |
| Effect on coefficients | Shrinks all coefficients toward zero; none set to exactly zero | Shrinks and sparsifies: some coefficients become exactly zero |
| Feature selection | Retains all features (still uses them all in predictions) | Automatic feature selection (Embedded method!); zeroed-out features are dropped |
| Interpretability | Less interpretable (all p features remain) | More interpretable (sparse, fewer non-zero coefficients) |
| Standardization | Strongly recommended (L2 penalty is very scale-sensitive) | Recommended (if skipped, regularization is applied unevenly across features) |
Trying to make the model perform better can make it more complex, and vice versa. \( \lambda \) is the "knob" that resolves this tension:
Elastic Net combines both penalties. A hyperparameter \( \rho \in [0, 1] \) (rho) weights the L1 and L2 terms:
Library notation note: In scikit-learn the mixing parameter is called l1_ratio (α in some textbooks). We use \( \rho \) here to avoid confusion with the gradient-descent learning rate α.
Gradient Boosting (Friedman, 2001) is a very popular ensemble method that combines multiple decision trees into a stronger model. The core idea is shared with AdaBoost: each weak learner tries to fix the mistakes of previous learners.
| Aspect | AdaBoost | Gradient Boosting |
|---|---|---|
| What gets "boosted" | Instance weights of misclassified points | Residuals (actual y − prediction of previous ensemble) |
| Weak-learner depth | Stumps (depth = 1) typically | Depth 2–8 is common; you set max_depth |
| Prediction combination | Weighted vote (per-tree α) | Additive: each tree's prediction is multiplied by a learning rate ν and summed |
Dataset of Age vs. Engagement score. We'll set: number of trees = 5 (so 4 additive trees), learning rate ν = 0.8, tree depth ≤ 2.
| Age | Engagement (y) |
|---|---|
| 10 | 7 |
| 20 | 5 |
| 30 | 7 |
| 40 | 1 |
| 50 | 2 |
| 60 | 1 |
| 70 | 5 |
| 80 | 4 |
The first "tree" is the simplest model: a single node that predicts the mean label for every point. Mean engagement = (7+5+7+1+2+1+5+4)/8 = 32/8 = 4.
The residual = true y minus current prediction. We train a regression tree of depth ≤ 2 to predict these residuals (the gaps Tree 1 failed to fill).
| Age | y | \( \hat{y}_1 \) | Residual (\( y - \hat{y}_1 \)) | \( \hat{y}_{\text{tree 2}} \) |
|---|---|---|---|---|
| 10 | 7 | 4 | +3 | 3 |
| 20 | 5 | 4 | +1 | 2 |
| 30 | 7 | 4 | +3 | 2 |
| 40 | 1 | 4 | −3 | −2.667 |
| 50 | 2 | 4 | −2 | −2.667 |
| 60 | 1 | 4 | −3 | −2.667 |
| 70 | 5 | 4 | +1 | +0.5 |
| 80 | 4 | 4 | 0 | +0.5 |
To avoid overfitting, we don't add Tree 2's full predictions. Instead, we multiply them by the learning rate before summing:
| y | \( \hat{y}_1 \) | \( \hat{y}_2 \) | \( 0.8 \cdot \hat{y}_2 \) | \( \hat{y}_{1+2} \) | New Residual (\( y - \hat{y}_{1+2} \)) |
|---|---|---|---|---|---|
| 7 | 4 | 3 | 2.4 | 6.4 | +0.6 |
| 5 | 4 | 2 | 1.6 | 5.6 | −0.6 |
| 7 | 4 | 2 | 1.6 | 5.6 | +1.4 |
| 1 | 4 | −2.667 | −2.13 | 1.87 | −0.87 |
| 2 | 4 | −2.667 | −2.13 | 1.87 | +0.13 |
| 1 | 4 | −2.667 | −2.13 | 1.87 | −0.87 |
| 5 | 4 | +0.5 | +0.4 | 4.4 | +0.6 |
| 4 | 4 | +0.5 | +0.4 | 4.4 | −0.4 |
We repeat the process: fit a new regression tree to the latest residuals, multiply its prediction by ν, add to the running total, recompute residuals, and continue. After many trees, the residuals get smaller and smaller — later weak learners only need to make tiny corrections, which is exactly what we want for stable generalization.
| Hyperparameter | What it controls | Guidance |
|---|---|---|
n_estimators |
Total number of trees in the ensemble (model complexity) | More trees → more complex (can overfit). Unlike Random Forests, more is NOT always better. Tune with learning_rate. |
learning_rate (ν) |
How strongly each tree corrects the mistakes of the previous trees | High ν → aggressive corrections, more complex fit. Low ν → slower, need more trees but often better generalization. Typical: 0.01–0.3. |
max_depth |
Max depth per individual tree (pre-pruning) | Controls complexity of each learner. Too deep → overfitting on its own. Typical: 2–8. |
Common practice: Fix n_estimators given your time/memory budget, then search over learning_rate values via cross-validation. Smaller learning rates usually need more trees — the two are coupled.
(For reference / labs. Not executed here.)
A. What model do we recover when λ = 0 in Ridge regression?
B. What happens as λ → +∞ in Lasso regression?
Pick the more appropriate regularization method for each goal.
Scenario A: You have 500 features and suspect only 20 of them matter; your stakeholders want a short, human-readable list of "the drivers" to put in a report.
Scenario B: You have 20 carefully chosen features from domain experts; each is known to be important. You just want to dampen coefficients and avoid overfitting, without dropping any feature.
A student sets learning_rate = 0.999 (essentially no shrinkage) and n_estimators = 1000. They get a training RMSE of 0.0001 but a validation RMSE that's huge. What happened, and how do you fix it?
Diagnosis: Extreme overfitting. With ν ≈ 1, each tree adds its FULL residual correction. 1,000 deep trees memorize every training point exactly (hence RMSE ≈ 0 on train), producing a spiky, non-generalizing model.
Fix:
Weights: \( \theta = [\theta_1 = 3,\ \theta_2 = -4]^T \). λ = 0.1, ρ = 0.6.
Step 1: L1 part (weighted by ρ):
Step 2: L2 part (weighted by 1 − ρ):
Step 3: Multiply by λ and sum:
Adding L2 regularization to the Normal Equation changes the closed-form solution to:
where \( I' \) is the identity matrix but with \( I'_{00} = 0 \) (bias is not regularized). Prove / reason: "Why does adding \( \lambda I' \) guarantee invertibility, even when \( X^T X \) is singular?"
Step 1: \( X^T X \) is always positive semi-definite: for any vector v, \( v^T X^T X v = \|Xv\|^2 \ge 0 \).
Step 2: Singularity ⟺ some non-zero v exists with \( \|Xv\| = 0 \) (i.e., X has linearly dependent columns).
Step 3: Adding \( \lambda I' \) (with λ > 0 and the bias trick) to \( X^T X \) shifts every eigenvalue of the feature-submatrix by λ. The result is positive definite:
One data point: y = 10. Tree 1 predicts mean = 5. Tree 2 predicts the residual (+5) perfectly. Learning rate ν = 0.2.
Step 1: Combined prediction after 2 trees:
Step 2: New residual (which Tree 3 will try to correct):
Even though Tree 2 perfectly predicted the +5 residual, we only add 20% of its correction on purpose. This "slow walk" to the answer is what prevents overfitting — each new tree only takes a small step toward reducing the residual.
Two models with the same 3 non-bias weights: θ = [5, 0, −5].
For each pair, which setting generally yields a more complex / overfitting-prone model?
Elastic Net penalty with λ = 0.5.
Your score: 0 / 5